@file:JvmName("CommonHelper")

package com.harvest.scientific.ladder.ext

import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Resources
import android.graphics.Rect
import android.os.Build
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.*
import com.google.gson.reflect.TypeToken
import com.harvest.scientific.ladder.base.ApGlobals
import com.harvest.scientific.ladder.util.GsUtil
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

fun View.getScreenWidth() = context.resources.displayMetrics.widthPixels
fun View.getScreenHeight() = context.resources.displayMetrics.heightPixels
fun getScreenWidth() = ApGlobals.get().resources.displayMetrics.widthPixels
fun getScreenHeight() = ApGlobals.get().resources.displayMetrics.heightPixels
fun getDisplayMetrics(): DisplayMetrics = ApGlobals.get().resources.displayMetrics
fun getPackageName(): String = ApGlobals.get().packageName

fun dp2px(dp: Float, resources: Resources): Float {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)
}

fun dp2px(dp: Float): Float {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, ApGlobals.get().resources.displayMetrics)
}

fun Context.hasPermission(permission:String) = checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED

fun above13(action: (() -> Unit)? = null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        action?.let {
            action()
        }
    }
}

fun below13(action: (() -> Unit)? = null) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
        action?.let {
            action()
        }
    }
}

fun above10(action: (() -> Unit)? = null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        action?.let {
            action()
        }
    }
}

fun below10(action: (() -> Unit)? = null) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        action?.let {
            action()
        }
    }
}

fun above9(action: (() -> Unit)? = null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        action?.let {
            action()
        }
    }
}

fun below9(action: (() -> Unit)? = null) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
        action?.let {
            action()
        }
    }
}

fun Boolean.ifTrue(action: () -> Unit) {
    if (this) {
        action()
    }
}

fun Boolean.ifFalse(action: () -> Unit) {
    if (!this) {
        action()
    }
}

val Context.asLifecycleOwner: FragmentActivity
    get() = this as FragmentActivity

val Context.asLifecycleOwnerOrNull: FragmentActivity?
    get() = this as? FragmentActivity

val View.asLifecycleOwnerOrNull: FragmentActivity?
    get() = this.context.asLifecycleOwnerOrNull

val View.asLifecycleOwner: FragmentActivity
    get() = this.context.asLifecycleOwner

val Context.scope: CoroutineScope
    get() = this.asLifecycleOwner.lifecycleScope

val Context.life: Lifecycle
    get() = this.asLifecycleOwner.lifecycle

fun Fragment.launch(action: suspend () -> Unit) {
    lifecycleScope.launch { action() }
}

fun launchOnMain(time: Long = 0L, action: suspend CoroutineScope.() -> Unit): Job {
    return launchOnCustom(CoroutineScope(Dispatchers.Main), time, action)
}

fun launchOnIO(time: Long = 0L, action: suspend CoroutineScope.() -> Unit): Job {
    return launchOnCustom(CoroutineScope(Dispatchers.IO), time, action)
}

fun launchOnCustom(scope: CoroutineScope, time: Long = 0L, action: suspend CoroutineScope.() -> Unit):Job {
    return scope.launch {
        if (time>0){//这个会丢协程线程池执行，在线程紧张的时候会new线程，如果此时线程数量也超限会崩溃，所以尽量避免这个操作
            delay(time)
        }
        action()
    }
}

fun customLaunch(action: suspend ()-> Unit) {
    CoroutineScope(Dispatchers.Main).launch {
        action()
    }
}

fun View.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
) {
    this.context.asLifecycleOwner.lifecycleScope.launch(context, start, block)
}

fun Context.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    return this.asLifecycleOwner.lifecycleScope.launch(context, start, block)
}



fun <T : View> T.visible(isVisible: Boolean = true, action: (T.() -> Unit)? = null) {
    visibility = if (isVisible) View.VISIBLE else View.GONE
    if (isVisible) {
        action?.let { this.action() }
    }
}

fun <T : View> T.visible2(isVisible: Boolean = true, action: (T.() -> Unit)? = null) {
    visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
    if (isVisible) {
        action?.let { this.action() }
    }
}
fun View.visible() {
    if (visibility != View.VISIBLE) {
        this.visibility = View.VISIBLE
    }
}

fun View.gone() {
    if (visibility != View.GONE) {
        this.visibility = View.GONE
    }
}

fun View.invisible() {
    if (visibility != View.INVISIBLE) {
        this.visibility = View.INVISIBLE
    }
}

fun visible(isVisible: Boolean, vararg views: View?) {
    views.forEach { it?.visibility = if (isVisible) View.VISIBLE else View.GONE }
}


fun View.parent() = (this.parent as? ViewGroup)

fun Any?.toJson(): String?= if (this == null) "" else GsUtil.toJson(this)

fun Any?.toBoolean() = this as? Boolean

inline fun <reified T> String.toObj() = GsUtil.fromJson(this, T::class.java)
inline fun <reified T> String.toArray() = GsUtil.fromJson<List<T>>(this, object : TypeToken<List<T>>() {}.type)
inline fun <reified K,V> String.toMap() = GsUtil.fromJson<Map<K,V>>(this, object : TypeToken<Map<K,V>>() {}.type)

fun String?.notEmpty() = !this.isNullOrEmpty()
fun CharSequence?.notEmpty() = !this.isNullOrEmpty()
fun Collection<*>?.notEmpty() = this != null && this.isNotEmpty()
fun getString(resId: Int) = ApGlobals.get().getString(resId)


val View.isInScreen: Boolean
    get() = ViewCompat.isAttachedToWindow(this) && visibility == View.VISIBLE && getLocalVisibleRect(
        Rect()
    )

fun <T> List<T>.pageIterator(pageSize: Int): Iterator<List<T>> {
    return object : Iterator<List<T>> {
        var pageIndex = 0
        override fun hasNext(): Boolean {
            return this@pageIterator.size > pageSize * pageIndex
        }

        override fun next(): List<T> {
            return this@pageIterator.subList(
                pageIndex * pageSize,
                if ((pageIndex + 1) * pageSize > this@pageIterator.size) this@pageIterator.size else (pageIndex + 1) * pageSize
            ).apply {
                pageIndex++
            }
        }
    }
}